package com.fiill.audioplayer;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.util.Log;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class AudioRecordManger {
    private static final String TAG = "BAHAL_AREC";
    private AudioRecord mAudioRecord;
    private DataOutputStream mDataOutputStream;
    private String mWavePath;
    private captureThread mRecordThread;
    private boolean isRecording = false;
    private static final int MIN_FRAME_SIZE = 480 * 2; // 2880 * 2

    /**
     * Singleton
     */
    private static AudioRecordManger mInstance;
    private final int mBufferSize;

    private AudioRecordManger() {
        //mBufferSize = AudioRecord.getMinBufferSize(WaveHeader.RATE, WaveHeader.IN_CHANNELS, WaveHeader.BIT_FORMAT);
        mBufferSize = MIN_FRAME_SIZE * Utils.getBitWidthFromFormat(WaveHeader.BIT_FORMAT
                      * AudioFormat.channelCountFromInChannelMask(WaveHeader.IN_CHANNELS));
    }

    public static AudioRecordManger getInstance() {
        if (mInstance == null) {
            synchronized (AudioRecordManger.class) {
                if (mInstance == null) {
                    mInstance = new AudioRecordManger();
                }
            }
        }
        return mInstance;
    }

    private String destroyThread() {
        String pcmPath = null;
        try {
            isRecording = false;
            if (mRecordThread != null && Thread.State.RUNNABLE == mRecordThread.getState()) {
                pcmPath  = mRecordThread.getPcmPath();
                try {
                    Thread.sleep(500);
                    mRecordThread.interrupt();
                } catch (Exception e) {
                    e.printStackTrace();
                    mRecordThread = null;
                }
            }
            mRecordThread = null;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return pcmPath;
    }

    /**
     * start capture thread
     *
     */
    private void startCaptureThread(String pcm_path) {
        destroyThread();
        isRecording = true;
        if (mRecordThread == null) {
            mRecordThread = new captureThread(pcm_path, mBufferSize);
            mRecordThread.start();
        }
    }

    class captureThread extends Thread {
        private final String mPcmPath;
        private final int mBufferSize;
        public captureThread(String pcm_path, int bufferSize) {
            mPcmPath = pcm_path;
            mBufferSize = bufferSize;
        }

        public String getPcmPath() {return mPcmPath;}

        @Override
        public void run() {
            try {
                android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
                int bytesRecord;
                byte[] tempBuffer = new byte[mBufferSize];

                if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
                    stopRecord();
                    return;
                }
                mAudioRecord.startRecording();
                while (isRecording) {
                    bytesRecord = mAudioRecord.read(tempBuffer, 0, mBufferSize);
                    if (bytesRecord == AudioRecord.ERROR_INVALID_OPERATION || bytesRecord == AudioRecord.ERROR_BAD_VALUE) {
                        continue;
                    }
                    if (bytesRecord != 0 && bytesRecord != -1) {
                        mDataOutputStream.write(tempBuffer, 0, bytesRecord);
                    } else {
                        break;
                    }
                }

            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    /**
     * save file
     */
    private void setPath(String pcm_path) throws Exception {
        File file = new File(pcm_path);
        if (file.exists()) {
            file.delete();
        }
        file.createNewFile();
        mDataOutputStream = new DataOutputStream(new FileOutputStream(file, true));
        Log.d(TAG, "Record pcm to: " + pcm_path);
    }

    /**
     * start to capture
     */
    public void startRecord(String wav_path) {
        if (isRecording) {
            Log.e(TAG, "Already capturing...");
            return;
        }
        mWavePath = wav_path;
        mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, WaveHeader.RATE,
                            WaveHeader.IN_CHANNELS, WaveHeader.BIT_FORMAT, mBufferSize);
        Log.i(TAG, "Create AudioRecord Rate:" + WaveHeader.RATE
                            + " Format:" + WaveHeader.BIT_FORMAT + " BufferSize:" + mBufferSize);
        try {
            String pcm_path  = wav_path + ".pcm";
            setPath(pcm_path);
            startCaptureThread(pcm_path);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * stop capturing
     */
    public void stopRecord() {
        if (!isRecording) {
            Log.e(TAG, "Not started...");
            return;
        }
        try {
            String pcm_path = destroyThread();
            if (mAudioRecord != null) {
                if (mAudioRecord.getState() == AudioRecord.STATE_INITIALIZED) {
                    mAudioRecord.stop();
                }
                if (mAudioRecord != null) {
                    mAudioRecord.release();
                    mAudioRecord = null;
                }
            }
            if (mDataOutputStream != null) {
                mDataOutputStream.flush();
                mDataOutputStream.close();
            }
            convertPCMFilesToWAVFile(pcm_path);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void convertPCMFilesToWAVFile(String pcm_path) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                boolean deleteTempFile = true;
                if (Utils.makePCMFileToWAVFile(pcm_path, mWavePath, deleteTempFile)) {
                    //success
                } else {
                    //failure
                    Log.e(TAG, "makePCMFileToWAVFile failed");
                    throw new IllegalStateException("makePCMFileToWAVFile failed");
                }
                mWavePath = null;
            }
        }).start();
    }
}
